/************************************************************************/
/*                     Copyright declaration                            */
/*                         (3-clause-BSD)                               */
/************************************************************************/

/*
* Copyright (c) 2009, Roc King
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the OwnWaterloo,Roc King nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "../../utility.h"

static int simple_function(void) { return 1212; }


void run_case(const storage_t* storage, int storage_privilege
             ,const promotion_t* copy,int copy_privilege
             ,const promotion_t* execute, int execute_privilege)
{
#define CODE_SIZE 86
    unsigned char stack_storage_dummy[CODE_SIZE];
    unsigned char* ptr = 0;
    int (*fn)(void) = 0;
    int result = 0;

    assert(storage && copy && execute);

    printf("allocate ");
    fflush(stdout);
    ptr = (unsigned char*)storage_allocate(storage,CODE_SIZE
        ,stack_storage_dummy,storage_privilege);
    if (!ptr) {
        printf("failed\n");
        return;
    }
    printf("ok\n");

    fn = (int (*)(void))(uintptr_t)ptr;
    
    printf("promotion before copy ");
    fflush(stdout);
    if (0!=promotion_promote(copy,ptr,CODE_SIZE,copy_privilege)) {
        printf("failed\n");
        goto DEALLOCATE;
    }
    printf("ok\n");

    printf("copy machine code ");
    fflush(stdout);
    memcpy(ptr,(const void*)(uintptr_t)&simple_function,CODE_SIZE);
    printf("ok\n");

    printf("promotion before execute ");
    fflush(stdout);
    if (0!=promotion_promote(execute,ptr,CODE_SIZE,execute_privilege)) {
        printf("failed\n");
        goto DEALLOCATE;
    }
    printf("ok\n");

    printf("about to jmp to machine code\n");
    printf("return = ");
    fflush(stdout);
    result = fn();
    printf("%d\n",result);

DEALLOCATE:
    storage_deallocate(storage,ptr);
    printf("deallocate ok\n");
    fflush(stdout);

    fprintf(stderr,"-------- test ends --------\n");
    fflush(stderr);

#undef CODE_SIZE
}

int main(int argc,char* argv[])
{
    struct {
        const char* category;
        const storage_t* handle;
        int privilege;
    } storage = {"stack",0,privilege_none};
    struct {
        const char* category;
        const promotion_t* handle;
        int privilege;
    } execute = {"null",0,privilege_none},copy = {"null",0,privilege_none};
    int result = -1;

    switch (argc)
    {
    case 1:
        break;
    default:
    case 7:
        execute.privilege = privilege_from_attribute(argv[4],",");
    case 6:
        execute.category = argv[3];
    case 5:
        copy.privilege = privilege_from_attribute(argv[6],",");
    case 4:
        copy.category = argv[5];
    case 3:
        storage.privilege = privilege_from_attribute(argv[2],",");
    case 2:
        storage.category = argv[1];
        break;
    }
    storage.handle = storage_get(storage.category);
    printf("storage :"
        "\ncategory=\"%15s\",exist=\"%6s\",privilege=\"%23s\"\n"
        ,storage.category,storage.handle?"true":"false"
        ,privilege_to_name(storage.privilege));

    copy.handle = promotion_get(copy.category);
    printf("promotion before copy :\n"
        "category=\"%15s\",exist=\"%6s\",privilege=\"%23s\"\n"
        ,copy.category,copy.handle?"true":"false"
        ,privilege_to_name(copy.privilege));

    execute.handle = promotion_get(execute.category);
    printf("promotion before execute :\n"
        "category=\"%15s\",exist=\"%6s\",privilege=\"%23s\"\n"
        ,execute.category,execute.handle?"true":"false"
        ,privilege_to_name(execute.privilege));

    fflush(stdout);
    if (storage.handle && copy.handle && execute.handle) {
        run_case(storage.handle,storage.privilege
                ,copy.handle,copy.privilege,execute.handle,execute.privilege);
        result = 0;
    }
    else {
        fprintf(stderr,"-------- invalid input --------\n");
        fflush(stderr);
    }
    printf("press Enter to exit\n");
    (void)getchar();
    return result;
}
